/*
muduo网络库给用户提供了两个主要的类
TcpServer:用于编写服务器程序的
TcpClient:用于编写客户端程序的
epoll + 线程池
好处:能够把网络的I/O代码和业务代码区分开来
                            暴露给业务的只有：用户的连接和断开    用户的可读写事件
*/
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <iostream> 
#include <functional>//绑定
#include <string>//绑定

using namespace std;
using namespace muduo;
using namespace muduo::net;
using namespace placeholders;//参数占位符
/*
基于muduo网络库开发网络器程序
1.组合TcpServer对象
2.创建EventLoop事件循环对象的指针，保存通过构造函数传进来的事件循环对象
3.明确TcpServer构造函数需要什么参数，输出ChatServer的构造函数
4.在当前服务器类的构造函数当中，注册处理连接的回调函数和处理读写事件的回调函数
5.设置合适的服务端线程数量，muduo库会自己分配I/O线程和worker线程
*/
/*
服务器类
*/
class ChatServer
{
    public:
          ChatServer(EventLoop* loop,//事件循环
                    const InetAddress& listenAddr,//IP+Port
                    const string& nameArg)//服务器名字
            : _server(loop,listenAddr,nameArg),_loop(loop)
            {
                
                //给服务器注册用户连接的创建和断开回调
                //由于setConnectionCallback()是没有返回值，一个形参变量，但是onConnection()是一个成员方法，它含一个this指针，因此需要绑定一下，添加头文件functional
                //把this指针和onConnection地址绑定
                //_1是占位符，需要添加头文件placeholders
                _server.setConnectionCallback(std::bind(&ChatServer::onConnection,this, _1));

                //给服务器注册用户读写事件回调
                _server.setMessageCallback(std::bind(&ChatServer::onMessage,this, _1, _2, _3));
                //设置服务器端的线程数量 1个I/O线程，3个worker线程
                _server.setThreadNum(4);
            }
            //开启事件循环
            void start()
            {
                _server.start();
            }
    private:
        //暴露的回调接口，专门处理用户的连接创建和断开
        //因为它已经注册到muduo库上了，当有用户连接和断开的时候，onConnection()方法就会响应
        //我们只需要在类的构造函数里设置回调函数，
        //注意形参conn不要忘记了，不要只写一个形参类型const TcpConnectionPtr& 在这里
        void onConnection(const TcpConnectionPtr& conn)
        {
            if(conn->connected())
            {
                //peer对端的
                cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << "state:online" << endl;
            }
            else
            {
                cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << "state:offline" << endl;
                //连接断开以后把socket释放掉
                conn->shutdown();//close(fd)
                //_loop->quit();表示连接断开
            }
            

        }
        //专门处理用户的读写事件
        //注意形参conn，buffer,time不要忘记了，不要只写形参类型在这里
        void onMessage(const TcpConnectionPtr &conn,//表示连接
                            Buffer *buffer,//缓冲区，一般写网络库都会写一个缓冲区，提高数据收发的性能
                            Timestamp time)//接收到数据的时间信息
        {
            string buf = buffer->retrieveAllAsString();
            cout<<"recv data:"<<buf<<"time:"<<time.toString()<<endl;
            conn->send(buf);
        }
        TcpServer _server; //#1
        EventLoop *_loop; //#2 epoll
        
};

int main()
{
    EventLoop loop;//epoll
    InetAddress addr("127.0.0.1",6000);
    ChatServer server(&loop,addr,"ChatServer");
    server.start();//启动服务，listenfd通过epoll_ctl=>epoll
    loop.loop();//epoll_wait以阻塞方式等待新用户连接，已连接用户的读写事件等
    return 0;
}